#include "pfnode.h"
#include <vector>
#include <boost/asio.hpp>
#include "trace.h"
#include "pfcmu_config.h"


PFNode::PFNode(std::string name, int totalThumbImg, QObject *parent) :
    TOTALCAMS(totalThumbImg), m_Fps(25), m_Width(640), m_Height(480),QObject(parent)
{
    m_IsLiving = false;
    m_IsCapturing = false;
    m_IsInit = true;
    m_IsRunning = true;
    m_WatchingCam = 0;
    m_SetShutter = false;
    m_SetGain = false;
    setNodeName(name);
    for(int i=0;i<totalThumbImg;i++)
    {
        GraphicsIplImgObject *thm = new GraphicsIplImgObject(PFCMU::WIDTH/4,PFCMU::HEIGHT/4);
        thm->setCurrentCam(i);
        thm->setThumb();
        thm->setTopTitle(0,m_NodeName);
        m_ThumbImg.push_back(thm);
        connect(thm,SIGNAL(objectDoubleClicked(int)),SLOT(setToWatching(int)));
    }
    m_LiveImg = new GraphicsIplImgObject();
    m_Shutter = PFCMU::DEFAULTSHUTTER;
    m_Gain = PFCMU::DEFAULTGAIN;
}

void PFNode::startConnection()
{
    m_IsInit = true;
    boost::thread netTrd(boost::bind(&PFNode::localClient, this,
                                     m_NodeName, INITPORT));
}

void PFNode::localClient(std::string nodeName, std::string nodePort)
{
    //realconnection
    /*Init*/
    try
    {
        using namespace boost::asio::ip;
        while(m_IsRunning)
        {
            tcp::iostream mainStream(nodeName, nodePort);
            TRACE(1, "To %s: Connected.\n", m_NodeName.c_str());
            std::string initCmd;
            std::getline(mainStream,initCmd);
            if(initCmd=="REDY")
            {
                //ok
                mainStream << "ENPF " << m_Fps <<" "
                           << m_Shutter <<" "<< m_Gain
                           << std::endl;
                std::string initLine;
                std::getline(mainStream, initLine);
                std::istringstream initIss(initLine);
                initIss >> initCmd;
                if(initCmd=="PFRY")
                {
                    unsigned int remoteFps;
                    initIss >> remoteFps;
                    TRACE(1, "Fm %s: ProFUSION Ready on %d fps.\n",
                          m_NodeName.c_str(),remoteFps);
                    m_IsInit = false;
                }
                else
                {
                    continue;
                }
            }//"REDY"
            else
            {
                continue;
            }

            /*main running loop*/
            while(m_IsRunning && !m_IsInit)
            {
                std::string mainRecvCmd;
                if(m_IsLiving)
                {
                    //live
                    std::vector<IplImage *> bufOrigFull(TOTALCAMS);
                    for(unsigned int i=0;i<TOTALCAMS;i++)
                    {
                        bufOrigFull[i] = cvCreateImage(cvSize(PFCMU::WIDTH,PFCMU::HEIGHT),
                                                       IPL_DEPTH_8U,1);
                        cvZero(bufOrigFull[i]);
                    }
                    mainStream << "ENLV " << m_WatchingCam << std::endl;
                    TRACE(1, "To %s: Live command sended\n", m_NodeName.c_str());
                    for(unsigned int i=0;i<TOTALCAMS;i++)
                    {
                        unsigned int recvWidth, recvHeight, recvIdx;
                        mainStream >> mainRecvCmd >> recvWidth
                                   >> recvHeight >> recvIdx;
                        mainStream.get();
                        if(mainRecvCmd=="FULL")
                        {
                            if(bufOrigFull[recvIdx]->width != recvWidth || bufOrigFull[recvIdx]->height != recvHeight)
                            {
                                //resize
                                IplImage *bufimg = cvCreateImage(cvSize(recvWidth,recvHeight),
                                                                 IPL_DEPTH_8U,1);
                                mainStream.read(bufimg->imageData,
                                                recvWidth*recvHeight);
                                cvReleaseImage(&bufimg);
                            }
                            else
                            {
                                mainStream.read(bufOrigFull[i]->imageData,
                                                recvWidth*recvHeight);
                            }
                        }
                        else
                        {
                            continue;
                        }
                    }//receiving finished
                    TRACE(1,"Fm %s: Images received\n", m_NodeName.c_str());
                    IplImage *bufFullColor = cvCreateImage(
                                cvSize(bufOrigFull[0]->width,bufOrigFull[0]->height),
                                IPL_DEPTH_8U,3);
                    IplImage *bufThumbBlack = cvCreateImage(
                                cvSize(m_ThumbImg.at(0)->getImageWidth(),m_ThumbImg.at(0)->getImageHeight()),
                                IPL_DEPTH_8U,1);
                    IplImage *bufThumbColor = cvCreateImage(
                                cvSize(m_ThumbImg.at(0)->getImageWidth(),m_ThumbImg.at(0)->getImageHeight()),
                                IPL_DEPTH_8U,3);
                    for(unsigned int i=0;i<TOTALCAMS;i++)
                    {
                        PFCMU::timestamp_t ts = PFCMU::get_timestamp(bufOrigFull[i]->imageData);

                        cvCvtColor(bufOrigFull[i],bufFullColor,CV_BayerGR2RGB);
                        cvResize(bufFullColor,bufThumbColor);
                        cvCvtColor(bufThumbColor,bufThumbBlack,CV_RGB2GRAY);
                        uint32_t thumbts = htobe32(ts);
                        *(reinterpret_cast<uint32_t *>(bufThumbBlack->imageData)) = thumbts;
                        IplImage *thumbhere = m_ThumbImg.at(i)->getOrigIplImage();
                        cvCopy(bufThumbBlack,thumbhere);
                    }
                    cvReleaseImage(&bufFullColor);
                    cvReleaseImage(&bufThumbBlack);
                    cvReleaseImage(&bufThumbColor);
                    IplImage *fullhere = m_LiveImg->getOrigIplImage();
                    cvCopy(bufOrigFull[m_WatchingCam],fullhere);
                    for(unsigned int i=0;i<bufOrigFull.size();i++)
                    {
                        cvReleaseImage(&(bufOrigFull[i]));
                    }
                    emit allImageDataReceived();
//                    for(unsigned int i=0;i<m_ThumbImg.size();i++)
//                    {
//                        m_ThumbImg.at(i)->update();
//                    }
//                    m_LiveImg->update();
                }
                else if(m_IsCapturing)
                {
                    //capture another loop
                    mainStream << "ENCP " << 0 <<" "
                               << m_CaptureFrames <<" "
                               << m_CaptureFName << std::endl;
                    std::string captureLine;
                    std::getline(mainStream,captureLine);
                    std::istringstream captureIss(captureLine);
                    captureIss >> mainRecvCmd;
                    if(mainRecvCmd=="CPFN")
                    {
                        PFCMU::timestamp_t captureendts = 0;
                        std::string captureendstr;
                        captureIss >> captureendts >> captureendstr;
                        TRACE(1, "Fm %s: Capture end at %llu. FileName: %s\n",
                              m_NodeName.c_str(), captureendts,captureendstr.c_str());
                        m_IsCapturing = false;
                        if(m_IsWatching)
                        {
                            m_IsLiving = true;
                        }
                    }
                    else
                    {
                        m_IsCapturing = false;
                    }
                }//capture
                else if(m_SetShutter)
                {
                    //set shutter
                }
                else if(m_SetGain)
                {
                    //set gain
                }
                else
                {
                    //cond.wait(mu);
                    continue;
                }
            }//main running loop
        }

    }
    catch(std::exception &e)
    {
        std::cerr << e.what() << std::endl;
    }
}

bool PFNode::checkReady() const
{
    return (m_IsRunning && !m_IsInit);
}

std::string PFNode::getNodeName() const
{
    return m_NodeName;
}

void PFNode::setNodeName(std::string name)
{
    m_NodeName = name;
}

double PFNode::getShutter() const
{
    return m_Shutter;
}

double PFNode::getGain() const
{
    return m_Gain;
}

QList<GraphicsIplImgObject *> PFNode::getThumbImages() const
{
    return m_ThumbImg;
}

GraphicsIplImgObject* PFNode::getLiveImage() const
{
    return m_LiveImg;
}

void PFNode::setShutter(double shutter)
{
    m_Shutter = shutter;
}



void PFNode::setGain(double gain)
{
    m_Gain = gain;
}



void PFNode::setToWatching(int cam)
{
    //boost::mutex::scoped_lock lock(mu);
    if(!m_IsCapturing)
    {
        m_IsLiving = true;
    }
    m_IsWatching = true;
    m_WatchingCam = cam;
    //cond.notify_all();
}

void PFNode::setNotWatching()
{
    //boost::mutex::scoped_lock lock(mu);
    m_IsLiving = false;
    m_IsWatching = false;
}

void PFNode::startCapture(QStringList paraCapture)
{
    //boost::mutex::scoped_lock lock(mu);
    if(!m_IsCapturing)
    {
        QString frames = paraCapture.at(0);
        QString fname = paraCapture.at(1);
        m_CaptureFrames = frames.toUInt();
        m_CaptureFName = fname.toStdString();
        m_IsLiving = false;
        m_IsCapturing = true;
    }
    //cond.notify_all();
}

int PFNode::watchingCamIdx() const
{
    return m_WatchingCam;
}


/*event*/
void PFNode::event_ConnectionEst(int fps)
{
    QString eventStr(this->m_NodeName.c_str());
    eventStr.append(": ");
    eventStr.append(QString("ProFUSION is running at %1 fps mode").arg(fps));
    emit clientEvent(eventStr);
}

void PFNode::event_CommandNotSend(std::string cmd)
{
    QString eventStr(this->m_NodeName.c_str());
    eventStr.append(": Command ");
    eventStr.append(QString(cmd.c_str()));
    eventStr.append(" is not sent");
    emit clientEvent(eventStr);
}
